package com.mswyt.utils.config.scan;

import com.mswyt.utils.base.MsAssertUtils;
import com.mswyt.utils.base.MsEmptyUtils;
import com.mswyt.utils.config.cache.MsCache;
import com.mswyt.utils.msConverts.MsEntityUtils;
import com.mswyt.utils.msCrudbean.annotation.MsCrudBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.util.Map;
import java.util.Set;

/**
 * @ClassName: MsScanRegister
 * @Description: 扫描配置，主要目的，在项目启动阶段时候，读取指定注解的实体信息，放入缓存
 */
@Slf4j
public class MsScanRegister implements ImportBeanDefinitionRegistrar {
    private final String RESOURCE_PATTERN = "/**/*.class";

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //获取扫描的包名称
        Map<String, Object> msScan = importingClassMetadata.getAnnotationAttributes(MsScan.class.getName());
        String[] backPages = (String[]) msScan.get("backPages");
        if (backPages.length != 0) {
            long begin = System.currentTimeMillis();
            log.info("===================开始解析@MsScan注解中指定包的class信息==================");
            for (String backPage : backPages) {
                cacheCustomA(backPage);
            }
            log.info("===================结束解析耗时：" + (System.currentTimeMillis() - begin) + "/ms==================");
        }
    }

    /**
     * 扫描指定包下的所有class
     *
     * @param packPage
     * @return
     * @author mashuai
     */

    private void cacheCustomA(String packPage) {
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        try {
            String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(packPage) + RESOURCE_PATTERN;
            Resource[] resources = resourcePatternResolver.getResources(pattern);
            MetadataReaderFactory readerfactory = new CachingMetadataReaderFactory(resourcePatternResolver);
            for (Resource resource : resources) {
                MetadataReader reader = readerfactory.getMetadataReader(resource);
                String classname = reader.getClassMetadata().getClassName();
                Class<?> clazz = Class.forName(classname);
                analyzeCrud(clazz);
            }
        } catch (IOException | ClassNotFoundException e) {
            log.info("解析@MsScan过程出现异常");
            MsAssertUtils.throwInnerException(e.getMessage());
        }
    }

    /**
     * 解析class属性
     **/
    private void analyzeCrud(Class<?> clazz) {
        //解析@MsCrud注解
        if (clazz.isAnnotationPresent(MsCrudBean.class)) {
            handleCrudA(clazz);
        }
    }

    /**
     * 处理crud注解
     **/
    private void handleCrudA(Class<?> clazz) {
        MsCrudBean annotation = clazz.getAnnotation(MsCrudBean.class);
        String targetParam = annotation.targetParam();
        if (MsEmptyUtils.isEmpty(targetParam)) {
            MsAssertUtils.throwInnerException("检测到在类名为：" + clazz.getName() + "，含有@MsCrudBean注解，但是其参数targetParam为空");
        }
        if (MsCache.crudCacheMap.get(targetParam) != null) {
            MsAssertUtils.throwInnerException("检测到在类名为：" + clazz.getName() + "，含有@MsCrudBean注解，但是其参数【targetParam = " + targetParam + "】的值已经存在了，请勿重复填写targetParam值");
        }
        //判断对应的
        MsCache.crudCacheMap.put(targetParam, annotation);
        MsCache.crudClassList.add(clazz);
        //缓存crud中需要排除的字段属性excludeFieldClass
        Class<?> excludeFieldClass = annotation.excludeFieldClass();
        String excludeFieldClassName = excludeFieldClass.getName();
        if (!Void.class.getName().equals(excludeFieldClassName)) {
            Set<String> sameFields = MsEntityUtils.getSameFields(clazz, excludeFieldClass);
            MsCache.crudExcludeFieldMap.put(clazz.getName(), sameFields);
        }
    }


}
